package cn.waleychain.exchange.service.impl.wallet.listener;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterNumber;
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.utils.Convert;
import org.web3j.utils.Convert.Unit;

import cn.waleychain.exchange.core.constant.DDIC;
import cn.waleychain.exchange.core.logger.LoggerHelper;
import cn.waleychain.exchange.core.utils.SequenceUtils;
import cn.waleychain.exchange.core.utils.StringUtils;
import cn.waleychain.exchange.feign.CoinServiceFeign;
import cn.waleychain.exchange.model.CoinInfo;
import cn.waleychain.exchange.model.CoinWallet;
import cn.waleychain.exchange.model.CoinWalletChange;
import cn.waleychain.exchange.model.WalletRecharge;
import cn.waleychain.exchange.service.impl.wallet.utils.EthCoinClient;
import cn.waleychain.exchange.service.wallet.CoinWalletService;

/**
 * 处理eth和eth代币的listener的监听
 */
public class EthReceiveListenerTwo implements Runnable {
	
	private static final Logger mLog = LoggerFactory.getLogger(EthReceiveListenerTwo.class);
	
    private CoinWalletService coinWalletService;//userInWallet的服务
    private CoinServiceFeign coinFeign;//币种服务
    private long coinId;
    private String coinName;
    private int lastBlockNumber;
    public HashMap<String, Long> contractInfo;
    public HashMap<String, Integer> contractMap;
    public HashMap<String, String> contractName;

    public EthReceiveListenerTwo(long coinId, CoinWalletService coinWalletService, CoinServiceFeign coinFeign) {
        this(null, coinId, "unknow(" + coinId + ")", 0, coinWalletService, coinFeign);
    }

    public EthReceiveListenerTwo(List<CoinInfo> tradeCoins, long coinId, String coinName, int sinceBlockNumber, CoinWalletService coinWalletService, CoinServiceFeign coinFeign) {
        this.coinId = coinId;
        this.coinName = coinName;
        this.lastBlockNumber = sinceBlockNumber;
        //this.lastBlockNumber = 4892930;
        this.coinWalletService = coinWalletService;
        this.coinFeign = coinFeign;
        
        LoggerHelper.printLogInfo(mLog, coinName + " receive listener");

        try {

            List<CoinInfo> ethToeknCoins = this.coinFeign.fetchCoinList(null, DDIC.WalletType.ETH.id, null);
            if (null != ethToeknCoins && ethToeknCoins.size() > 0) {
                contractInfo = new HashMap<>();
                contractMap = new HashMap<>();
                contractName = new HashMap<>();
                for (CoinInfo item : ethToeknCoins) {
                    if (item.getCoinId() > 0 && StringUtils.isNotBlank(item.getContractAddress())) {
                        contractInfo.put(item.getContractAddress(), item.getCoinId());
                        contractMap.put(item.getContractAddress(), item.getAmountUnit());
                        contractName.put(item.getContractAddress(), item.getName());
                    }
                }
            }

        } catch (Exception e) {
            LoggerHelper.printLogErrorNotThrows(mLog, e, "[" + coinName + "]spring get bean error:" + e.getMessage());
        }

    }


    @SuppressWarnings("rawtypes")
	public void run() {
        int currentBlockNumber = 0;

        try {
            CoinInfo tradeCoin = this.coinFeign.fetchCoinById(this.coinId);//eth和eth的代币都是用的同一个
            if (tradeCoin != null) {
                Web3j web3j = EthCoinClient.getCoinClient(tradeCoin);

                int latest = web3j.ethBlockNumber().send().getBlockNumber().intValue() - 15;//获取当前区块高度,减去12

                currentBlockNumber = this.lastBlockNumber;//之前数据库保存最后更新的区块高度

                for (int i = currentBlockNumber + 1; i < latest; ++i) {

                    List<EthBlock.TransactionResult> block = web3j.ethGetBlockByNumber(new DefaultBlockParameterNumber(i), true).send().getBlock().getTransactions();
                    if (null == block || block.size() == 0) {//如果区块高度为null,或者个数为0,继续看下一个
                        continue;
                    }
                    
                    LoggerHelper.printLogInfo(mLog, "区块开始,高度为:" + i);
                    
                    CoinWallet userEth = null;
                    for (EthBlock.TransactionResult item : block) {
                        EthBlock.TransactionObject transaction = (EthBlock.TransactionObject) item;
                        String input = transaction.getInput();
                        if (StringUtils.isBlank(transaction.getTo())) {
                            continue;
                        }
                        String to = transaction.getTo().toLowerCase();
                        BigDecimal money = null;
                        long nowCoinId = this.coinId;
                        //首先判断是不是合约地址,如果使我们的合约地址,真正的发送地址

                        String nowCoinName = this.coinName;
                        if (contractInfo.containsKey(to)) {//解析合约
                            nowCoinId = contractInfo.get(to);
                            nowCoinName = contractName.get(to);
                            if (StringUtils.isNotBlank(input) && input.length() >= 138) {//136表示合约的最低长度
                                String moneyStr = input.substring(74, 138);//金额长度64
                                money = new BigDecimal(new BigInteger(moneyStr, 16)).divide(BigDecimal.TEN.pow(contractMap.get(to)));
                                to = "0x" + input.substring(34, 74);//发送的地址,长度是64
                                transaction.setTo(to);
                            }
                            
                            LoggerHelper.printLogInfo(mLog, "to:" + transaction.getTo() + "  txid:" + transaction.getHash() + "  coinid:" + nowCoinId);
                            
                            userEth = this.coinWalletService.fetchWalletByCoinIdAndAddr(nowCoinId, transaction.getTo());
                            
                            if (null != userEth) {
                            	LoggerHelper.printLogInfo(mLog, "查询到用户信息, coinid:" + nowCoinId + "   to:" + transaction.getTo());
                            }
                        } else if ("0x".equals(input)) {
                        	userEth = this.coinWalletService.fetchWalletByCoinIdAndAddr(nowCoinId, transaction.getTo());
                            money = Convert.fromWei(new BigDecimal(transaction.getValue()), Unit.ETHER);
                        } else {
                            continue;
                        }


                        if (null != userEth && transaction.getValue() != null && null != money && money.compareTo(BigDecimal.ZERO) > 0) {
                        	LoggerHelper.printLogInfo(mLog, "查询到一条记录,交易id为:" + transaction.getHash());
                        	
                            WalletRecharge userInWallet = coinWalletService.fetchWalletRechargeByTxIdAndAddress(transaction.getHash(), userEth.getAddress());
                            if (null == userInWallet) {
                            	TransactionReceipt transactionReceipt = web3j.ethGetTransactionReceipt(transaction.getHash()).send().getTransactionReceipt().get();
                                if (transactionReceipt == null || "0x0".equals(transactionReceipt.getStatus())) {
                                	LoggerHelper.printLogInfo(mLog, "充值失败,交易id为:" + transaction.getHash());
                                }else {
                                	WalletRecharge inRecord = new WalletRecharge();
                                    inRecord.setIdNo(SequenceUtils.generateDefaultSerialNum());
                                    inRecord.setCoinWalletId(userEth.getCoinWalletId());
                                    inRecord.setAddress(transaction.getTo());
                                    inRecord.setAmount(money);
                                    inRecord.setFee(new BigDecimal("0"));
                                    inRecord.setRealAmount(money);
                                    inRecord.setTxId(transaction.getHash());
                                    inRecord.setStatus(-12);
                                    
                                    CoinWalletChange change = new CoinWalletChange();
                                    change.setCoinWalletId(userEth.getCoinWalletId());
                                    change.setBizType(DDIC.WalletBizType.WALLET_RECHARGE.id);
                                    change.setBizTypeId(inRecord.getIdNo());
                                    change.setType(DDIC.AccountChangeType.INCOME_1.id);
                                    change.setStatus(DDIC.WalletChangeStatus.RECHARGE_SUSS.id);
                                    change.setCreateTime(new Date());
                                    
                                    if (this.coinWalletService.addWalletRecharge(inRecord, change)) {
                                    	LoggerHelper.printLogInfo(mLog, "[" + nowCoinName + "]receiveConfirmed");
                                    }
                                }
                            } else {
                            	LoggerHelper.printLogInfo(mLog, "记录已经提交不要重复充值id为:" + transaction.getHash());
                            }
                        }
                    }
                    currentBlockNumber = i;
                }

                return;
            }
        } catch (Exception e) {
            LoggerHelper.printLogErrorNotThrows(mLog, e, this.coinName + " receive error:" + e.getMessage());
            return;
        } finally {
            if (currentBlockNumber > this.lastBlockNumber) {
                try {
                    this.lastBlockNumber = currentBlockNumber;
                    CoinInfo coin = new CoinInfo();
                    coin.setCoinId(this.coinId);
                    coin.setLastblock(currentBlockNumber + "");
                    this.coinFeign.modifyCoinInfo(coin);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }

    }

}